<script setup lang="ts">
import { type Component, computed, h, inject, nextTick, type PropType, provide, type Ref, shallowRef, type VNode, watch } from "vue";
import type { ComponentType, LazyComponent } from "@/framework/vue/types";
import type { ToggleItemCustomHooks } from "@/components/framework/toggle-view-helper";

const props = defineProps({
  // 组件ID
  itemId: { required: true, type: String },
  // 组件
  component: { required: true, type: [Object, Function] as PropType<ComponentType | LazyComponent> },
  // 是否启用缓存
  cacheEnabled: { default: () => true, type: Boolean },
});

const activeKey = inject<Ref<string>>("toggle-view:active-key");
const visible = computed(() => activeKey?.value == props.itemId);
const content = shallowRef<VNode>();
const contentRef = shallowRef<any>();

const getContentVNode = async () => {
  if (typeof props.component === "function") {
    try {
      const resp = await (props.component as () => Promise<{ default: ComponentType }>)();
      return h(resp.default);
    } catch (e) {
      return h("span", { style: { color: "red" } }, "Component error");
    }
  }
  return h(props.component as Component);
};

const hooks = {
  onShow: [] as Function[],
  onHide: [] as Function[],
};

provide("toggle-item-custom-hooks", <ToggleItemCustomHooks>{
  onShow: (fn: Function) => hooks.onShow.push(fn),
  onShowOnce: (fn: Function) => {
    const _fn = () => {
      fn();
      hooks.onShow.splice(hooks.onShow.indexOf(_fn), 1);
    };
    hooks.onShow.push(_fn);
  },
  removeShowHandler: (fn: Function) => hooks.onShow.splice(hooks.onShow.indexOf(fn), 1),
  onHide: (fn: Function) => hooks.onHide.push(fn),
  onHideOnce: (fn: Function) => {
    const _fn = () => {
      fn();
      hooks.onHide.splice(hooks.onHide.indexOf(_fn), 1);
    };
    hooks.onHide.push(_fn);
  },
  removeHideHandler: (fn: Function) => hooks.onHide.splice(hooks.onHide.indexOf(fn), 1),
});

watch(
  visible,
  async value => {
    if (value) {
      // 显示
      if (content.value == undefined) {
        content.value = await getContentVNode();
      } else {
        void nextTick(() => {
          hooks.onShow.forEach(fn => fn());
        });
      }
    } else {
      // 隐藏
      if (!props.cacheEnabled) {
        content.value = undefined;
      } else {
        void nextTick(() => {
          hooks.onHide.forEach(fn => fn());
        });
      }
    }
  },
  { immediate: true },
);
</script>

<template>
  <template v-if="cacheEnabled">
    <component v-show="visible && content" :is="content" ref="contentRef" />
  </template>
  <template v-else>
    <component v-if="visible && content" :is="content" />
  </template>
</template>
